/*
 * Description: performance manager
 */

#include <thread>
#include <functional>
#include "common/guest_utils.h"
#include "error.h"
#include "log.h"
#include "perf_pipe_reciever.h"
#include "process_monitor_injector.h"
#include "perf_manager.h"
#include "performance/autorun/autorun_manager.h"
#include "performance/config/perf_config_factory.h"
#include "performance/utils/registry_utils.h"
#include "performance/utils/perf_utils.h"
#include <Windows.h>

using namespace COMMON;

namespace PERF {

bool PerfManager::m_stopListeningPerfPipe = false;

constexpr const int SITE_CONFIG_MAX_RETRY_TIMES = 20;
constexpr const DWORD SITE_CONFIG_RETRY_INTERVAL = 5;
constexpr const DWORD MILLI_PER_SECOND = 1000;
constexpr const DWORD DOMAIN_VALUE_ALLOWED_SITE = 2;

ErrorCode PerfManager::Init()
{
    m_stopListeningPerfPipe = false;
    LOG_INFO("[PERF - Init] - creating tRecvingPipeMessage");
    std::thread tRecvingPipeMessage(Listen);
    tRecvingPipeMessage.detach();
    std::thread tAllowedSiteConfig(Configure);
    tAllowedSiteConfig.detach();
    return OK;
}

ErrorCode PerfManager::Stop()
{
    LOG_INFO("[PERF - Stop] - Performance pipe listening stopping");
    m_stopListeningPerfPipe = true;
    auto config = PERF_CONFIG_FACTORY.GetConfig();
    if (config != nullptr) {
        for (auto entry : config->moduleNameToAutoruns) {
            AutorunManager::RemoveAutorun(entry.second);
        }
    }
    PERF_CONFIG_FACTORY.ClearConfig();
    PROCESS_MONITOR_INJECTOR.Uninject();
    return OK;
}

void PerfManager::Listen()
{
    while (!PerfManager::m_stopListeningPerfPipe) {
        PERF_PIPE_RECIEVER.RecvMessage();
    }
    LOG_INFO("[PERF - Listen] - Performance pipe listening stopped.");
}

void PerfManager::ApplyAllowedSites(const HKEY &hKeyDomains)
{
    auto config = PERF_CONFIG_FACTORY.GetConfig();
    if (!config) {
        return;
    }
    for (AllowedSite &site : config->allowedSites) {
        if (site.domain.size() == 0) {
            continue;
        }
        HKEY hKeyDomain = nullptr;
        if (!REGISTRY_UTILS.CreateRegKey(hKeyDomains, site.domain.c_str(), hKeyDomain)) {
            LOG_WARN("[PERF - WriteAllowedSites] - Create reg key failed");
            continue;
        }
        for (std::string &protocol : site.protocols) {
            if (protocol.size() == 0) {
                continue;
            }
            REGISTRY_UTILS.SetRegDWORD(hKeyDomains, site.domain.c_str(), protocol.c_str(), DOMAIN_VALUE_ALLOWED_SITE);
        }
        REGISTRY_UTILS.SafeCloseHKey(hKeyDomain);
    }
}

void PerfManager::Configure()
{
    int t = 0;
    std::string userSid;
    while (++t < SITE_CONFIG_MAX_RETRY_TIMES) {
        userSid = PERF_UTILS.GetCurrentUserSid();
        if (userSid == "") {
            LOG_WARN("[PERF - Configure] - get current user sid failed, retry times: [{}/{}]", t,
                     SITE_CONFIG_MAX_RETRY_TIMES);
            Sleep(SITE_CONFIG_RETRY_INTERVAL * MILLI_PER_SECOND);
            continue;
        }
        break;
    }
    if (userSid == "") {
        LOG_WARN("[PERF - Configure] - get user sid failed");
        return;
    }
    std::string domainSubKey =
        userSid + R"(\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains)";
    HKEY hKeyDomains = nullptr;
    if (!REGISTRY_UTILS.GetRegKey(HKEY_USERS, domainSubKey.c_str(), hKeyDomains) || hKeyDomains == nullptr) {
        LOG_WARN("[PERF - Configure] - get domain subkey failed");
        return;
    }
    ApplyAllowedSites(hKeyDomains);
    REGISTRY_UTILS.SafeCloseHKey(hKeyDomains);
}

}  // namespace PERF